package firebase

import (
	"fmt"
	"net/url"
	"regexp"
	"strings"
)

var (
	bareTopicNamePattern  = regexp.MustCompile("^[a-zA-Z0-9-_.~%]+$")
	colorPattern          = regexp.MustCompile("^#[0-9a-fA-F]{6}$")
	colorWithAlphaPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$")
)

func validateMessage(message *Message) error {
	if message == nil {
		return fmt.Errorf("message must not be nil")
	}

	targets := countNonEmpty(message.Token, message.Condition, message.Topic)
	if targets != 1 {
		return fmt.Errorf("exactly one of token, topic or condition must be specified")
	}

	// validate topic
	if message.Topic != "" {
		bt := strings.TrimPrefix(message.Topic, "/topics/")
		if !bareTopicNamePattern.MatchString(bt) {
			return fmt.Errorf("malformed topic name")
		}
	}

	// validate Notification
	if err := validateNotification(message.Notification); err != nil {
		return err
	}

	// validate AndroidConfig
	return validateAndroidConfig(message.Android)
}

func validateNotification(notification *Notification) error {
	if notification == nil {
		return nil
	}

	image := notification.ImageURL
	if image != "" {
		if _, err := url.ParseRequestURI(image); err != nil {
			return fmt.Errorf("invalid image URL: %q", image)
		}
	}
	return nil
}

func validateAndroidConfig(config *AndroidConfig) error {
	if config == nil {
		return nil
	}

	if config.TTL != nil && config.TTL.Seconds() < 0 {
		return fmt.Errorf("ttl duration must not be negative")
	}
	if config.Priority != "" && config.Priority != "normal" && config.Priority != "high" {
		return fmt.Errorf("priority must be 'normal' or 'high'")
	}

	// validate AndroidNotification
	return validateAndroidNotification(config.Notification)
}

func validateAndroidNotification(notification *AndroidNotification) error {
	if notification == nil {
		return nil
	}
	if notification.Color != "" && !colorPattern.MatchString(notification.Color) {
		return fmt.Errorf("color must be in the #RRGGBB form")
	}
	if len(notification.TitleLocArgs) > 0 && notification.TitleLocKey == "" {
		return fmt.Errorf("titleLocKey is required when specifying titleLocArgs")
	}
	if len(notification.BodyLocArgs) > 0 && notification.BodyLocKey == "" {
		return fmt.Errorf("bodyLocKey is required when specifying bodyLocArgs")
	}
	image := notification.ImageURL
	if image != "" {
		if _, err := url.ParseRequestURI(image); err != nil {
			return fmt.Errorf("invalid image URL: %q", image)
		}
	}
	for _, timing := range notification.VibrateTimingMillis {
		if timing < 0 {
			return fmt.Errorf("vibrateTimingMillis must not be negative")
		}
	}

	return nil
}

func countNonEmpty(strings ...string) int {
	count := 0
	for _, s := range strings {
		if s != "" {
			count++
		}
	}
	return count
}
